#ifndef _XTXTLOADER_H_
#define _XTXTLOADER_H_
/******************************************************************************/
/*                                                                            */
/*  File: xtxtloader.h                                                        */
/*  Author: bkenwright@xbdev.net                                              */
/*  URL: www.xbdev.net                                                        */
/*                                                                            */
/******************************************************************************/
#pragma comment(lib, "D3d9.lib")
#pragma comment(lib, "D3dx9.lib")
#pragma warning (disable : 4996)
#pragma warning (disable : 4099)

#include <d3dx9.h>
#include <stdio.h>

#define DBG_HALT __asm{ int 0x10 }
#define DBG_ASSERT(exp) {if ( !(exp) ) {DBG_HALT;}}



#define X_FILE						0xFC
#define X_BASE						0xFD
#define X_UNKNOWN               	0xFE            // unknown
#define X_TEMPLATE					0xFF			// template
#define X_ERROR                 	0x0
#define X_HEADER					0x1				// Header
#define X_VECTOR					0x2				// Vector
#define X_COORDS2D					0x3				// Coords2d
#define X_QUATERNION				0x4				// Quaternion
#define X_MATRIX4X4					0x5				// Matrix4x4
#define X_COLORRGBA					0x6				// ColorRGBA
#define X_COLORRGB					0x7				// ColorRGB
#define X_INDEXEDCOLOR				0x8				// IndexedColor
#define X_BOOLEAN					0x9				// Boolean
#define X_BOOLEAN2D					0xA				// Boolean2d
#define X_MATERIAL					0xB				// Material
#define X_TEXTUREFILENAME			0xC				// TextureFilename
#define X_MESHFACE					0xD				// MeshFace
#define X_MESHFACEWRAPS				0xE				// MeshFaceWraps
#define X_MESHTEXTURECOORDS			0xF				// MeshTextureCoords
#define X_MESHNORMALS				0x10			// MeshNormals 
#define X_MESHVERTEXCOLORS			0x11			// MeshVertexColors
#define X_MESHMATERIALLIST			0x12			// MeshMaterialList
#define X_MESH						0x13			// Mesh
#define X_FRAMETRANSFORMMATRIX		0x14			// FrameTransformMatrix
#define X_FRAME						0x15			// Frame
#define X_FLOATKEYS					0x16			// FloatKeys
#define X_TIMEDFLOATKEYS			0x17			// TimedFloatKeys
#define X_ANIMATIONKEY				0x18			// AnimationKey
#define X_ANIMATIONOPTIONS			0x19			// AnimationOptions
#define X_ANIMATION					0x1A			// Animation
#define X_ANIMATIONSET				0x1B			// AnimationSet
#define X_SKINWEIGHTS           	0x1C            // SkinWeights
#define X_XSKINMESHHEADER       	0x1D            // XSkinMeshHeader
#define X_XOF						0x1E			// xof
#define X_VERTEXDUPLICATIONINDICES	0x1F			// VertexDuplicationIndices
#define	X_EFFECTINSTANCE			0x20			// EffectInstance
#define X_EFFECTPARAMSTRING			0x21			// EffectParamString
#define X_EFFECTPARAMDWORD			0x22			// EffectParamDWord
#define X_EFFECTPARAMFLOATS			0x23			// EffectParamFloats
#define X_PATCH						0x24			// Patch
#define X_PATCHMESH					0x25			// PatchMesh

struct stDefines
{
	int		id;
	char*	name;
};

static stDefines xtxttypes[] =
{
	{  X_TEMPLATE,					"template"					},
	{  X_HEADER,					"Header"					},
	{  X_VECTOR,					"Vector"					},
	{  X_COORDS2D,					"Coords2d"					},
	{  X_QUATERNION,				"Quaternion"				},
	{  X_MATRIX4X4,					"Matrix4x4"					},
	{  X_COLORRGBA,					"ColorRGBA"					},
	{  X_COLORRGB,					"ColorRGB"					},
	{  X_INDEXEDCOLOR,				"IndexedColor"				},
	{  X_BOOLEAN,					"Boolean"					},
	{  X_BOOLEAN2D,					"Boolean2d"					},
	{  X_MATERIAL,					"Material"					},
	{  X_TEXTUREFILENAME,			"TextureFilename"			},
	{  X_MESHFACE,					"MeshFace"					},
	{  X_MESHFACEWRAPS,				"MeshFaceWraps"				},
	{  X_MESHTEXTURECOORDS,			"MeshTextureCoords"			},
	{  X_MESHNORMALS,				"MeshNormals"				},
	{  X_MESHVERTEXCOLORS,			"MeshVertexColors"			},
	{  X_MESHMATERIALLIST,			"MeshMaterialList"			},
	{  X_MESH,						"Mesh"						},
	{  X_FRAMETRANSFORMMATRIX,		"FrameTransformMatrix"		},
	{  X_FRAME,						"Frame"						},
	{  X_FLOATKEYS,					"FloatKeys"					},
	{  X_TIMEDFLOATKEYS,			"TimedFloatKeys"			},
	{  X_ANIMATIONKEY,				"AnimationKey"				},
	{  X_ANIMATIONOPTIONS,			"AnimationOptions"			},
	{  X_ANIMATION,					"Animation"					},
	{  X_ANIMATIONSET,				"AnimationSet"				},
	{  X_SKINWEIGHTS,           	"SkinWeights"           	},
	{  X_XSKINMESHHEADER,       	"XSkinMeshHeader"       	},
	{  X_XOF,						"xof"						},
	{  X_VERTEXDUPLICATIONINDICES,	"VertexDuplicationIndices"	},
	{  X_EFFECTINSTANCE,			"EffectInstance"			},
	{  X_EFFECTPARAMSTRING,			"EffectParamString"			},
	{  X_EFFECTPARAMDWORD,			"EffectParamDWord"			},
	{  X_EFFECTPARAMFLOATS,			"EffectParamFloats"			},
	{  X_PATCH,						"Patch"						},
	{  X_PATCHMESH,					"PatchMesh"					},
};

/******************************************************************************/

struct stXBase
{
	virtual int GetType() const { return X_BASE; }
	virtual ~stXBase(){};
};

/******************************************************************************/

struct stXSkinWeights
{
	stXSkinWeights()
	{
		strcpy_s(m_name, "NONAMESET");
		m_next			= NULL;
		m_numIndexes	= 0;
		m_indexes		= NULL;
		m_weights		= NULL;
		D3DXMatrixIdentity(&m_mat);
	}
	~stXSkinWeights()
	{
		delete[] m_indexes;
		delete[] m_weights;
	}

	char			m_name[64];
	stXSkinWeights* m_next;
	int				m_numIndexes;
	int*			m_indexes;
	float*			m_weights;
	D3DXMATRIX		m_mat;
};

/******************************************************************************/

struct stXMaterialList
{
	stXMaterialList()
	{
		m_numMaterials = 0;
		m_numIndexes   = 0;
		m_indexes	   = NULL;

	}
	~stXMaterialList()
	{
		delete[] m_indexes;
	}

	int  m_numMaterials;
	int  m_numIndexes;
	int* m_indexes;

};


/******************************************************************************/

struct stXMesh : public stXBase
{
	int GetType() const { return X_MESH; }

	stXMesh()
	{
		m_next				= NULL;
		m_numVerts			= 0;
		m_verts				= NULL;
		m_numTris			= 0;
		m_tris				= NULL;
		m_skinWeights		= NULL;
		m_matList			= NULL;
		m_numNormals		= 0;
//		m_normals			= NULL;
//		m_numNormalIndxs	= 0;
//		m_normalIndxs		= NULL;
		m_numTexCoords		= 0;
		m_texCoords			= NULL;
	}
	~stXMesh()
	{
		delete[] m_verts;		m_verts  =NULL;
		delete[] m_tris;		m_tris	 =NULL;
		delete   m_matList;		m_matList=NULL;
//		delete[] m_normals;		m_normals=NULL;
//		delete[] m_normalIndxs; m_normalIndxs=NULL;
		delete[] m_texCoords;	m_texCoords=NULL;

		stXSkinWeights* tmp = m_skinWeights;
		while (tmp)
		{
			stXSkinWeights* child = tmp;
			tmp = tmp->m_next;
			delete child;
			child = NULL;
		}

	}

	void AddSkinWeight(stXSkinWeights* skinWeights)
	{
		DBG_ASSERT(skinWeights);
		if (m_skinWeights)
		{
			stXSkinWeights* tmp = m_skinWeights;
			while (tmp)
			{
				if (tmp->m_next==NULL)
				{
					break;
				}
				tmp = tmp->m_next;
			}
			tmp->m_next = skinWeights;
		}
		else
		{
			m_skinWeights = skinWeights;
		}
	}

	stXMesh*			m_next;

	int					m_numVerts;
	D3DXVECTOR3*		m_verts;

	struct stTriData
	{
		int				matId;
		int				vertIndx[3];
		D3DXVECTOR3		normals[3];
	};
	int					m_numTris;
	stTriData*			m_tris;

//	int					m_numTris;
//	unsigned int*		m_indices;	// 3 * m_numTris

	int					m_numTexCoords;
	D3DXVECTOR2*		m_texCoords;


	int					m_numNormals;
//	D3DXVECTOR3*		m_normals;	 
//	int					m_numNormalIndxs;
//	int*				m_normalIndxs;


	stXSkinWeights* 	m_skinWeights;
	stXMaterialList*	m_matList;


};

/******************************************************************************/

struct stXFrame : public stXBase
{
	int GetType() const { return X_FRAME; }

	stXFrame()
	{
		m_next  = NULL;
		m_child = NULL;
		m_mesh  = NULL;
		D3DXMatrixIdentity(&m_mat);
		memset(m_name, 0, sizeof(m_name));
		static int count = 0;
		count++;
		sprintf_s(m_name, "NOFRAMENAME%06d", count);
		D3DXMatrixIdentity(&m_matTrans);
		D3DXMatrixIdentity(&m_matAnim);
	}

	~stXFrame()
	{
		stXFrame* tmp = m_child;
		while (tmp)
		{
			stXFrame* child = tmp;
			tmp = tmp->m_next;
			delete child;
			child = NULL;
		}
		stXMesh* tmpMesh = m_mesh;
		while (tmpMesh)
		{
			stXMesh* child = tmpMesh;
			tmpMesh = tmpMesh->m_next;
			delete child;
			child = NULL;
		}
	}

	void AddChild(stXFrame* xFrame)
	{
		DBG_ASSERT(xFrame);

		if (m_child==NULL)
		{
			m_child = xFrame;
		}
		else
		{
			if (m_child->m_next)
			{
				stXFrame* tmp = m_child->m_next;
				while (tmp && tmp->m_next)
				{
					tmp = tmp->m_next;
				}
				tmp->m_next = xFrame;
			}
			else
			{
				m_child->m_next = xFrame;
			}
		}
	}

	void AddMesh(stXMesh* xMesh)
	{
		DBG_ASSERT(xMesh);
		if (m_mesh)
		{
			stXMesh* tmp = m_mesh;
			while (tmp)
			{
				if (tmp->m_next==NULL)
				{
					break;
				}
				tmp = tmp->m_next;
			}
			tmp->m_next = xMesh;
		}
		else
		{
			m_mesh = xMesh;
		}
	}


	stXFrame*	m_next;
	stXFrame*	m_child;

	stXMesh*	m_mesh;
	D3DXMATRIX  m_mat;
	D3DXMATRIX	m_matTrans;
	D3DXMATRIX  m_matAnim;
	char		m_name[128];
};

/******************************************************************************/



struct stXMaterial : stXBase
{
	int GetType() const { return X_MATERIAL; }
	stXMaterial()
	{
		m_id	= -1;
		memset(m_textureFileName, 0, sizeof(m_textureFileName));
		m_hasTexture = false;
		memset(m_name, 0, sizeof(m_name));
		static int uniqueName = 0;
		uniqueName++;
		DBG_ASSERT(uniqueName<100000);
		sprintf_s(m_name, "RandomName%06d", uniqueName);
		m_next	= NULL;
		m_pTexture = NULL;
	}
	~stXMaterial()
	{
		if (m_pTexture)
		{
			m_pTexture->Release();
			m_pTexture = NULL;
		}
	}

	void LoadTexture(IDirect3DDevice9* pDevice, const char* folder)
	{
		if (!m_hasTexture) return;

		int len = (int)strlen(m_textureFileName);

		int iIndex = len;
		while( (iIndex>0) && (m_textureFileName[iIndex--]!= '\\' ) );
		DBG_ASSERT(iIndex>=0 && iIndex<127);

		DBG_ASSERT(len<127);
		DBG_ASSERT(iIndex>=0 && iIndex<127);
		char szFileName[256] = {0};
		int folderLen = (int)strlen(folder);
		DBG_ASSERT(folderLen+len < 255);
		strcpy_s( szFileName, folder);
		strcat_s( szFileName, &(m_textureFileName[iIndex]));

		HRESULT hr = 
		D3DXCreateTextureFromFileA(	pDevice, 
									szFileName, 
									&m_pTexture );
	}

	int					m_id;
	char				m_name[128];
	stXMaterial*		m_next;
	float				m_faceColourRGBA[4];
	float				m_power;
	float				m_specularColourRGB[3];
	float				m_emmisiveColourRGB[3];
	bool				m_hasTexture;
	char				m_textureFileName[128];

	IDirect3DTexture9*	m_pTexture;
};

struct stMaterialManager
{
	stMaterialManager()
	{
		m_materials = NULL;
	}

	~stMaterialManager()
	{
		stXMaterial* tmp = m_materials;
		while (tmp)
		{
			stXMaterial* mat = tmp;
			tmp = tmp->m_next;
			delete mat;
			mat = NULL;
		}
	}

	stXMaterial* AddMaterial()
	{
		stXMaterial* newMat = new stXMaterial();
		int id = 0;
		if (m_materials)
		{
			stXMaterial* tmp = m_materials;
			while (tmp)
			{
				id++;
				if (tmp->m_next==NULL)
				{
					break;
				}
				tmp = tmp->m_next;
			}
			tmp->m_next = newMat;
		}
		else
		{
			m_materials = newMat;
		}

		newMat->m_id = id;
		return newMat;
	}

	stXMaterial* FindMaterial(const char* matName) const
	{
		stXMaterial* mat = m_materials;
		while (mat)
		{
			if (_stricmp(mat->m_name, matName)==0)
			{
				return mat;
			}
			mat = mat->m_next;
		}
		return NULL;
	}

	stXMaterial* GetMaterial(const int id) const
	{
		stXMaterial* mat = m_materials;
		while (mat)
		{
			if (mat->m_id == id)
			{
				return mat;
			}
			mat = mat->m_next;
		}
		return NULL;

	}

	void LoadTextures(IDirect3DDevice9* pDevice, char* folder)
	{
		stXMaterial* mat = m_materials;
		while (mat)
		{
			mat->LoadTexture(pDevice, folder);
			mat = mat->m_next;
		}

	}


	stXMaterial* m_materials;
};

/******************************************************************************/

struct stXAnimationKey : stXBase
{
	int GetType() const { return X_ANIMATIONKEY; }

	~stXAnimationKey()
	{
		delete[] m_mat;
	}

	int			m_numFrames;
	D3DXMATRIX*	m_mat;
};

/******************************************************************************/

struct stXAnimation
{
	stXAnimation()
	{
		m_next = NULL;
		memset(m_name, 0, sizeof(m_name));
		static int count = 0;
		sprintf_s(m_name, "NOANIMNAMESET%06d", count);
		count++;

		memset(m_skinName, 0, sizeof(m_skinName));
		static int countS = 0;
		sprintf_s(m_skinName, "NOSKINNAME%06d", countS);
		countS++;

		m_animKey = NULL;
	}
	~stXAnimation()
	{
		delete m_animKey;
	}

	stXAnimation*		m_next;
	char				m_name[128];
	char				m_skinName[128];
	stXAnimationKey*	m_animKey;
};

/******************************************************************************/

struct stXAnimSet : public stXBase
{
	int GetType() const { return X_ANIMATIONSET; }
	stXAnimSet()
	{
		memset(m_name, 0, sizeof(m_name));
		static int count = 0;
		sprintf_s(m_name, "NONAME%06d", count++);

		m_next=NULL;
		m_anims=NULL;
	}
	~stXAnimSet()
	{
		if (m_next)
		{
			delete m_next;
		}
		stXAnimation* tmp = m_anims;
		while (tmp)
		{
			stXAnimation* xa = tmp;
			tmp = tmp->m_next;
			delete xa;
			xa=NULL;
		}

	}

	void AddNext(stXAnimSet* xAnimSet)
	{
		DBG_ASSERT(xAnimSet);
		if (m_next)
		{
			stXAnimSet* tmp = m_next;
			while (tmp)
			{
				if (tmp->m_next==NULL)
				{
					break;
				}
				tmp = tmp->m_next;
			}
			tmp->m_next = xAnimSet;
		}
		else
		{
			m_next = xAnimSet;
		}
	}

	void AddAnimation(stXAnimation* anim)
	{
		DBG_ASSERT(anim);
		if (m_anims)
		{
			stXAnimation* tmp = m_anims;
			while (tmp)
			{
				if (tmp->m_next==NULL)
				{
					break;
				}
				tmp = tmp->m_next;
			}
			tmp->m_next = anim;
		}
		else
		{
			m_anims = anim;
		}
	}

	stXAnimSet*		m_next;
	char			m_name[128];
	stXAnimation*	m_anims;
};

/******************************************************************************/

inline stXAnimation* FindAnimation(const stXAnimSet* xAnimSet, const char* boneName)
{
	stXAnimation* tmp = xAnimSet->m_anims;
	while (tmp)
	{
		if ( strcmp(boneName, tmp->m_skinName)==0 )
		{
			return tmp;
		}

		tmp = tmp->m_next;
	}
	return NULL;
}

inline const stXFrame* FindFrame(const stXFrame* xFrame, const char* boneName)
{
	const stXFrame* tmp = xFrame;
	while (tmp)
	{
		if (tmp->m_child)
		{
			const stXFrame* foundFrame = FindFrame(tmp->m_child, boneName);
			if (foundFrame)
			{
				return foundFrame;
			}
		}

		if ( strcmp(boneName, tmp->m_name)==0 )
		{
			return tmp;
		}

		tmp = tmp->m_next;
	}
	return NULL;
}


struct stXFile : public stXBase
{
	int GetType() const { return X_FILE; }

	stXFrame			m_frame;
	stXAnimSet*			m_animSet;

	stXFile()
	{
		m_animSet		= NULL;
		m_frameNo		= 0;
		m_curAnimSet	= NULL;
		m_pD3DDevice	= NULL;
		memset(m_fileName, 0, sizeof(m_fileName));
		strcpy_s(m_fileName, "NOFILENAMESET");
	}
	~stXFile()
	{
		delete m_animSet;
		m_animSet	 = NULL;
		m_pD3DDevice = NULL;
	}

	stMaterialManager	m_materialManager;


	int					m_frameNo;
	stXAnimSet*			m_curAnimSet;
	IDirect3DDevice9*	m_pD3DDevice;
	char				m_fileName[128];


};

/******************************************************************************/

void LoadXTxtFile(IDirect3DDevice9* pDevice, stXFile* xFile, const char* fileName);

extern bool g_staticPose;


inline void BuildFrameMatHierachy(const stXFile* xFile, const stXFrame* xFrameParent, stXFrame* xFrame)
{
	DBG_ASSERT(xFile);
	DBG_ASSERT(xFrame);

	D3DXMATRIX parentTransMat;
	D3DXMATRIX parentAnimMat;
	D3DXMatrixIdentity(&parentTransMat);
	D3DXMatrixIdentity(&parentAnimMat);
	if (xFrameParent)
	{
		parentTransMat = xFrameParent->m_matTrans;
		parentAnimMat  = xFrameParent->m_matAnim;
	}

	while (xFrame)
	{
		// Default Mat
		D3DXMATRIX animMat = xFrame->m_mat;

		// If we have an Anim, replace our default one with it
		const char* frameName = xFrame->m_name;
		const stXAnimSet* xAnimSet = xFile->m_curAnimSet;
		if (xAnimSet && !g_staticPose)
		{
			const stXAnimation* anim = FindAnimation(xAnimSet, frameName);
			if (anim)
			{
				int frameNo			= xFile->m_frameNo;

				DBG_ASSERT(anim);
				DBG_ASSERT(anim->m_animKey);
				int numFrames = anim->m_animKey->m_numFrames;
				DBG_ASSERT(numFrames>0 && numFrames>frameNo);
				
				animMat = anim->m_animKey->m_mat[ frameNo ];

				/*
				static float offf = 5.0f;

				if (strcmp(frameName, "Box03")==0)
				{
					animMat._42 += offf;
				}

				static float offf2 = 100.0f;

				if (strcmp(frameName, "Line01")==0)
				{
					animMat._42 += offf2;
				}
				*/
			}
		}

		xFrame->m_matTrans = xFrame->m_mat * parentTransMat;
		xFrame->m_matAnim  = animMat * parentAnimMat;

		if (xFrame->m_child)
		{
			BuildFrameMatHierachy(xFile, xFrame, xFrame->m_child);
		}

		xFrame = xFrame->m_next;
	}
}


#endif // _XTXTLOADER_H_
